import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class TreeParser {

	private ParseNode rootNode;
	private static ParserUtils parserUtils;
	private static HashMap<String, String> mapeamento;
	private static LinkedList<Funcao> funcoes;

	private LinkedList<AcaoSemantica> acoes;

	public TreeParser(ParseNode rootNode) {
		this.rootNode = rootNode;
		this.acoes = new LinkedList<AcaoSemantica>();
		funcoes = new LinkedList<Funcao>();
		parserUtils = new ParserUtils();
		this.populaMapeamento();
	}

	private void populaMapeamento() {
		mapeamento = new HashMap<String, String>();
		mapeamento.put("expr_without_variable", "trataExprWithoutVariable");
		mapeamento.put("cvar", "trataCVar");
		mapeamento.put("expr", "trataExpr");
		mapeamento.put("unticked_statement", "trataUntickedStatement");
		mapeamento.put("unticked_declaration_statement", "trataUntickedDeclarationStatement");
	}

	public static void adicionaFuncao(Funcao f) {
		if (f != null) {
			funcoes.addLast(f);
		}
	}
	
	public void parse() {
		parseHelper(rootNode, acoes);
	}

	public static void parseHelper(ParseNode no, LinkedList<AcaoSemantica> lista) {
		List<ParseNode> children = no.getChildren();
		for (ParseNode parseNode : children) {
			if (parseNode.toString().equals("unticked_statement") && parseNode
					.getNumChildren() == 2 && parseNode.getChild(0).isToken()) {
				Generico g = new Generico(parseNode.getChild(0).getLexeme());
				lista.addLast(g);
			} else if (!mapeamento.containsKey(parseNode.toString())
					|| (parseNode.toString().equals("unticked_statement") && (parseNode
							.getNumChildren() != 7
							&& parseNode.getNumChildren() != 5 && parseNode
							.getNumChildren() != 9))) {
				parseHelper(parseNode, lista);
			} else {
				String methodName = mapeamento.get(parseNode.toString());
				try {
					Method m = parserUtils.getClass().getMethod(methodName,
							ParseNode.class);
					Object r = m.invoke(parserUtils, parseNode);
					if (r instanceof AcaoSemantica) {
						AcaoSemantica as = (AcaoSemantica) r;
						lista.addLast(as);
					}
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public String getCode() {
		StringBuffer sb = new StringBuffer();
		
		Iterator<Funcao> it2 = funcoes.iterator();
		while (it2.hasNext()) {
			Funcao as = it2.next();
			sb.append(as.getCode());
			sb.append(System.lineSeparator());
			sb.append(System.lineSeparator()); // TODO remover.. botei so pra
												// ficar mais facil de entender
												// o codigo
		}
		
		Iterator<AcaoSemantica> it = acoes.iterator();
		while (it.hasNext()) {
			AcaoSemantica as = it.next();
			sb.append(as.getCode());
			sb.append(System.lineSeparator());
			sb.append(System.lineSeparator()); // TODO remover.. botei so pra
												// ficar mais facil de entender
												// o codigo
		}
		return sb.toString();
	}

}
